package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.agent.SystemApiAgent;
import com.xuecheng.api.content.model.CourseBaseDTO;
import com.xuecheng.api.content.model.TeachplanDTO;
import com.xuecheng.api.system.model.dto.CourseCategoryDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.common.constant.CoursePubTemplateKey;
import com.xuecheng.content.convert.CoursePubConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.entity.CoursePub;
import com.xuecheng.content.entity.CoursePubMsg;
import com.xuecheng.content.mapper.CoursePubMapper;
import com.xuecheng.content.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {
    @Autowired
    private CourseBaseService courseBaseService;
    @Autowired
    private CourseMarketService courseMarketService;
    @Autowired
    private TeachplanService teachplanService;
    @Autowired
    private SystemApiAgent systemApiAgent;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private CoursePubMsgService coursePubMsgService;
    @Value("${course.publish.exchange}")
    private String exchange;
    @Value("${course.publish.routingkey}")
    private String routingkey;



    /*
    * 主方法业务：
    * 1.构建CoursePub数据并保存
    *
    * 2.根据CoursePub内容构架DataMap数据
    *
    * 3.将DataMap进行返回
       /**
        * 课程预览
        *
        * @param courseId  课程Id
        * @param companyId 公司Id
        * @return Map 数据模型 Map
        */
    @Override
    @Transactional
    public Map<String, Object> previewCourse(Long courseId, Long companyId) {
        //1.构建CoursePub数据并保存
        CoursePub coursePub = generateCoursePub(courseId,companyId,false);
        // 2.根据CoursePub内容构架DataMap数据
        Map<String,Object> dataMap = generateDataMap(coursePub);
        // 3.将DataMap进行返
        return dataMap;
    }

    /*
     * 构建CoursePub数据并保存
     *   业务分析：
     *       1.判断关键数据
     *        courseId companyId
     *       2.判断业务数据
     *           课程基础信息
     *               判断是否存在
     *               判断是否是同一家机构
     *               判断是否删除
     *               判断审核状态：教学机构课程预览--未提交、审核未通过
     *           课程营销
     *               判断是否存在：根据courseid
     *           课程计划
     *               获得课程计划：根据courseId和companyId（树形结构）
     *           课程教师
     *               判断教师信息是否存在：一定要确保课程最少有一个教师信息
     *
     *           课程分类数据并完善Coursepub数据
     *               调用system服务获得课程分类的名称
     *
     *       3.保存数据的业务数据和消息数据
     *           业务数据：CoursePub数据进行保存
     *           消息数据：CoursePubMsg数据进行保存
     *
     *       4.发送消息给MQ
     *
     * */
    @Override
    @Transactional
    public void publish(Long courseBaseId, Long companyId, boolean isResend) {

        //1.判断关键数据
        //  courseId companyId
        if (ObjectUtils.isEmpty(courseBaseId)||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        CoursePubMsg coursePubMsg = null;

        // 判断是否是从新发送消息，如果是，没有必要创建CoursePub和CoursePubMsg数据了
        if (isResend) {
            // 查询课程发布消息表的数据即可
            LambdaQueryWrapper<CoursePubMsg> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(CoursePubMsg::getCourseId,courseBaseId);
            queryWrapper.eq(CoursePubMsg::getCompanyId,companyId);

            coursePubMsg = coursePubMsgService.getOne(queryWrapper);

            // 如果在定时器重新发送消息时，消息表的数据不存在
            // 记录消息并结束该方法
            if (ObjectUtils.isEmpty(coursePubMsg)) {
                log.error(ContentErrorCode.E_120203.getDesc()+",courseBaseId 信息：{}",courseBaseId);
                return;
            }


        } else {

            // 前端的课程发布业务操作会调用，需要生成CoursePub和CoursePubMSG数据
            // 2.判断业务数据
            //     课程基础信息
            //         判断是否存在
            //         判断是否是同一家机构
            //         判断是否删除
            //         判断审核状态：教学机构课程预览--未提交、审核未通过
            //     课程营销
            //         判断是否存在：根据courseid
            //     课程计划
            //         获得课程计划：根据courseId和companyId（树形结构）
            //     课程教师
            //         判断教师信息是否存在：一定要确保课程最少有一个教师信息
            //     课程分类数据并完善Coursepub数据
            //         调用system服务获得课程分类的名称
            CoursePub coursePub = this.generateCoursePub(courseBaseId, companyId,true);


            // 3.保存数据的业务数据和消息数据
            //     业务数据：CoursePub数据进行保存(已经在generateCoursePub进行了保存，无需操作)
            //     消息数据：CoursePubMsg数据进行保存
            coursePubMsg = new CoursePubMsg();
            coursePubMsg.setPubId(coursePub.getId());
            coursePubMsg.setCourseId(courseBaseId);
            coursePubMsg.setCompanyId(coursePub.getCompanyId());
            coursePubMsg.setPubName(coursePub.getName());
            // 发送消息默认的状态：未发送成功
            coursePubMsg.setPubStatus(CoursePubMsg.UNSENT);
            boolean result = coursePubMsgService.save(coursePubMsg);

            if (!result) {
                ExceptionCast.cast(ContentErrorCode.E_120206);
            }

        }
        // 4.发送消息给MQ
        /*
         * 参数：
         *   1.交换机的名称
         *   2.rontingkey
         *   3.消息
         *       消息表的数据：CoursePubMsg（json）
         *   4.CorrelationData(设置confirm callback)
         *       接受交互机处理数据的结果（应答机制）
         *           ack：交换机处理成功--> 修改业务数据和消息表的状态
         *           nack：交换机处理失败--> 记录错误消息，并不操作业务数据和消息表
         * */
        /* 构造方法需要指定消息唯一标识：业务数据作为依赖数据 （CoursePubId）*/
        CorrelationData correlationData = new CorrelationData(coursePubMsg.getPubId().toString());

        correlationData.getFuture().addCallback(
                /* mq的交换机接受消息，但不知道是否处理成功 */
                confirm -> {
                    boolean ack = confirm.isAck();
                    if (ack) {
                        // 修改业务数据和消息表的状态
                        execuChangeLocalStatusData(correlationData.getId());
                    } else {
                        // 记录错误消息，并不操作业务数据和消息表
                        log.error(ContentErrorCode.E_120206.getDesc()+",业务数据为CoursePubId：{}",correlationData.getId());
                    }
                },
                /* mq的服务异常，没有接收到消息 */
                throwable -> {
                    log.error(ContentErrorCode.E_120207.getDesc()+",业务数据为CoursePubId：{}  erroMsg: {}",
                            correlationData.getId(),
                            throwable.getMessage());
                }
        );

        String msgJsonString = JsonUtil.objectTojson(coursePubMsg);

        rabbitTemplate.convertAndSend(exchange,routingkey,msgJsonString,correlationData);

    }

    /*
     * 修改本地数据的状态值：
     *       0.判断消息是否已经修改
     *       1.CourseBase
     *           审核状态：课程已发布
     *       2.CoursePubMsg
     *           发送成功的状态：0->1
     * */
    @Transactional
    public  void execuChangeLocalStatusData(String coursePubId) {

        // 0.判断消息是否已经修改
        // 查询coursePubMsg表中的数据并判断其状态
        // CoursePubMsg中的id和CoursePub表的id是唯一、一致
        // select * from course_pub_msg where pubid = ? and pubStatus = ?
        LambdaQueryWrapper<CoursePubMsg> pubMsgQueryWrapper = new LambdaQueryWrapper<>();
        pubMsgQueryWrapper.eq(CoursePubMsg::getPubId, new Long(coursePubId));
        pubMsgQueryWrapper.eq(CoursePubMsg::getPubStatus, CoursePubMsg.UNSENT);

        CoursePubMsg coursePubMsg = coursePubMsgService.getOne(pubMsgQueryWrapper);
        if (ObjectUtils.isEmpty(coursePubMsg)) {
            log.info("修改消息数据已经处理，无需操作，CoursPubId：{}",coursePubId);
            return;
        }



        // 1. CourseBase
        // 审核状态：课程已发布
        Long courseId = coursePubMsg.getCourseId();

        // update course_base set auditStatus = ? where id = ?
        LambdaUpdateWrapper<CourseBase> baseUpdateWrapper = new LambdaUpdateWrapper<>();
        baseUpdateWrapper.set(CourseBase::getAuditStatus,CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode());
        baseUpdateWrapper.eq(CourseBase::getId, courseId);

        boolean baseResult = courseBaseService.update(baseUpdateWrapper);
        if (!baseResult) {
            log.error("修改消课程审核状态失败，CoursPubId：{}",coursePubId);
            return;
        }


        // 2.CoursePubMsg
        // 发送成功的状态：0->1
        // update set course_pub_msg set pub_status = ? where pub_id = ?
        LambdaUpdateWrapper<CoursePubMsg> pubMsgUpdateWrapper = new LambdaUpdateWrapper<>();
        pubMsgUpdateWrapper.set(CoursePubMsg::getPubStatus,CoursePubMsg.SENT);
        pubMsgUpdateWrapper.eq(CoursePubMsg::getPubId, coursePubMsg.getPubId());

        boolean pubResult = coursePubMsgService.update(pubMsgUpdateWrapper);
        if (!pubResult) {
            log.error("修改消息表的状态失败，CoursPubId：{}",coursePubId);

            // 要保证数据的一致性
            ExceptionCast.cast(ContentErrorCode.E_120206);
        }


    }

    private Map<String, Object> generateDataMap(CoursePub coursePub) {

        // 1.构建数据模型对象
        HashMap<String, Object> dataMap = new HashMap<>();

        // 2.构建coursePub
        dataMap.put(CoursePubTemplateKey.COURSEPUB, coursePub);

        // 3.构建courseMarket
        String marketJsonString = coursePub.getMarket();
        CourseMarket courseMarket = JsonUtil.jsonToObject(marketJsonString, CourseMarket.class);

        dataMap.put(CoursePubTemplateKey.COURSEMARKET,courseMarket );

        // 4.构建课程计划
        String teachplanJsonString = coursePub.getTeachplan();
        TeachplanDTO teachplanDTO = JsonUtil.jsonToObject(teachplanJsonString, TeachplanDTO.class);
        dataMap.put(CoursePubTemplateKey.TEACHPLANNODE,teachplanDTO );

        // 5.构建课程模式
        CourseModeEnum[] values = CourseModeEnum.values();
        dataMap.put(CoursePubTemplateKey.COURSETEACHMODEENUMS,values );

        return dataMap;
    }


    /*
     * 构建CoursePub数据并保存
     *   业务分析：
     *       1.判断关键数据
     *        courseId companyId
     *       2.判断业务数据
     *           课程基础信息
     *               判断是否存在
     *               判断是否是同一家机构
     *               判断是否删除
     *               判断审核状态：教学机构课程预览--未提交、审核未通过
     *           课程营销
     *               判断是否存在：根据courseid
     *           课程计划
     *               获得课程计划：根据courseId和companyId（树形结构）
     *           课程教师
     *               判断教师信息是否存在：一定要确保课程最少有一个教师信息
     *
     *           课程分类数据并完善Coursepub数据
     *               调用system服务获得课程分类的名称
     *
     *       3.保存课程发布数据
     *           CoursePub数据保存数据库中
     *               课程基础信息  课程营销  课程计划  课程教师
     *
     *       4.将coursePub数据返回
     *
     * */
    private CoursePub generateCoursePub(Long courseId, Long companyId,boolean isResend) {

        //1.判断关键数据
        //  courseId companyId

        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否是同一家机构
        //         判断是否删除
        //         判断审核状态：教学机构课程预览--未提交、审核未通过
        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(courseId, companyId);

        String auditStatus = courseBase.getAuditStatus();
        if (!isResend){
            if ((CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
                    CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
                    CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
            )) {
                ExceptionCast.cast(ContentErrorCode.E_120015);
            }
        }else {
            if (!CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)
            ) {
                ExceptionCast.cast(ContentErrorCode.E_120029);
            }
        }


        //     课程营销
        //         判断是否存在：根据courseid  (courseBaseService.getCourseBaseById 里已经对其进行判断是否存在)
        LambdaQueryWrapper<CourseMarket> marketQueryWrapper = new LambdaQueryWrapper<>();
        marketQueryWrapper.eq(CourseMarket::getCourseId, courseId);

        CourseMarket courseMarket = courseMarketService.getOne(marketQueryWrapper);


        //     课程计划
        //         获得课程计划：根据courseId和companyId（树形结构）

        TeachplanDTO teachplanTreeNodes = teachplanService.queryTreeNodesByCourseId(courseId, companyId);


        //     课程教师
        //         判断教师信息是否存在：一定要确保课程最少有一个教师信息
        // TODO: 2021/12/17 学员完成




        //     课程分类数据并完善Coursepub数据
        //         调用system服务获得课程分类的名称
        String mt = courseBase.getMt();
        String st = courseBase.getSt();

        RestResponse<CourseCategoryDTO> mtResponse = systemApiAgent.getById(mt);

        if (!(mtResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(mtResponse.getCode(),mtResponse.getMsg());
        }

        RestResponse<CourseCategoryDTO> stResponse = systemApiAgent.getById(st);

        if (!(stResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(stResponse.getCode(),stResponse.getMsg());
        }

        CourseCategoryDTO mtEntity = mtResponse.getResult();
        CourseCategoryDTO stEntity = stResponse.getResult();


        // 构建coursePub数据并执行保存操作
        // 课程基础数据
        CoursePub coursePub = CoursePubConvert.INSTANCE.courseBase2coursePub(courseBase);

        // 课程营销数据
        String marketJsonString = JsonUtil.objectTojson(courseMarket);
        coursePub.setMarket(marketJsonString);
        coursePub.setPrice(courseMarket.getPrice());
        coursePub.setCharge(courseMarket.getCharge());

        // 课程计划数据
        String teachplanJsonString = JsonUtil.objectTojson(teachplanTreeNodes);
        coursePub.setTeachplan(teachplanJsonString);

        // 课程教师
        // TODO: 2021/12/17 学员完成


        // 课程分类数据
        coursePub.setMtName(mtEntity.getName());
        coursePub.setStName(stEntity.getName());


//        if (!isResend){
            // 3.保存课程发布数据
            //     CoursePub数据保存数据库中
            //         课程基础信息  课程营销  课程计划  课程教师
            // 一个courseBase数据对应一个coursePub数据
            LambdaQueryWrapper<CoursePub> pubQueryWrapper = new LambdaQueryWrapper<>();
            pubQueryWrapper.eq(CoursePub::getCourseId, courseId);
            pubQueryWrapper.eq(CoursePub::getCompanyId, companyId);

            CoursePub po = this.getOne(pubQueryWrapper);

            boolean result = false;
            // 判断课程发布数据是否存在
            if (ObjectUtils.isEmpty(po)) {
                //     如果不存在
                //             创建课程发布数据
                //     创建课程发布数据时，要将coursePub和courseBase数据进行关联
                coursePub.setCourseId(courseBase.getCourseBaseId());
                result = this.save(coursePub);

            } else {
                //     如果存在
                //             修改课程发布数据内容
                coursePub.setId(po.getId());

                result = this.updateById(coursePub);

            }

            if (!result) {
                ExceptionCast.cast(ContentErrorCode.E_120205);
            }

       // }


        // 4.将coursePub数据返回
        return coursePub;
    }
}
